import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
import {clipboard} from 'electron'

import {AddCodeBlockModal} from './AddCodeBlockModal'
// Remember to rename these classes and interfaces!

interface PluginSettings {
	mySetting: string;
	debugMode: boolean;
}

type Headline = [number, string]

const DEFAULT_SETTINGS: PluginSettings = {
	mySetting: 'default',
	debugMode: false,
}

class QuickPlugin extends Plugin {
	settings: PluginSettings;
	editor: Editor

	_debug() {
		if (this.settings.debugMode) {
			let editor = this.app.workspace.getActiveViewOfType(MarkdownView).editor
			// Object.defineProperty(this.app,'editor',{value:editor})
			// window['editor'] = editor
			console.log('Add property editor')
		}
	}

	_log(...args: any[]) {
		if (this.settings.debugMode) {
			new Notice(args.join(''))
		}

	}

	_extract_headline_info(line: string): Headline {
		let headline_reg = /^(?<level>#+)\s(?<text>.*)$/
		let res = line.match(headline_reg)?.groups
		let head_level = res?.level?.length
		let text = res?.text
		return [head_level, text]
	}

	_make_new_headline(headline: Headline, level_up: boolean) {
		let level = headline[0]
		if (!level) {
			return ''
		}
		console.log(level_up)
		let new_level = level_up ? level - 1 : level + 1
		console.log('level->new level:', level, '->', new_level)
		new_level = Math.clamp(new_level, 1, 6)
		if (new_level == level) {
			return ''
		}
		return '#'.repeat(new_level) + ' ' + headline[1]
	}


	_set_single_headline_level(editor: Editor, level_up: boolean) {
		let cursor = editor.getCursor()
		let cur_line = editor.getLine(cursor.line)
		let headline = this._extract_headline_info(cur_line)
		if (!headline[0]) {
			this._log('There is no headline.')
			return
		}
		let new_line = this._make_new_headline(headline, level_up)
		if (!new_line) {
			this._log('The level of headline can not be changed!')
			return
		}
		this._log('Set new headline:', new_line)
		editor.setLine(cursor.line, new_line)
	}

	_set_selection_headline_level(editor: Editor, level_up: boolean) {
		let cursor_from = editor.getCursor('from')
		let cursor_to = editor.getCursor('to')
		let from_line = Math.min(cursor_from.line, cursor_to.line)
		let to_line = Math.max(cursor_from.line, cursor_to.line)
		let count = 0
		for (let index = from_line; index < to_line + 1; index++) {
			let line = editor.getLine(index)
			let headline = this._extract_headline_info(line)
			if (headline[0]) {
				let new_line = this._make_new_headline(headline, level_up)
				if (new_line) {
					editor.setLine(index, new_line)
					count++
				}
			}
		}
		this._log('Change healine level count:', count)
	}

	_add_same_level_headline(editor: Editor) {
		if (editor.somethingSelected()) {
			this._log('Can not add headline with something selected!')
			return
		}
		let line_idx = editor.getCursor().line
		let line = editor.getLine(line_idx)
		let up_idx = line_idx - 1
		let prev_level = 1
		while (up_idx >= 0) {
			let prev_line = editor.getLine(up_idx)
			let prev_h = this._extract_headline_info(prev_line)
			if (prev_h[0]) {
				prev_level = prev_h[0]
				break
			}
			up_idx--
		}

		if (line) {
			let headline = this._extract_headline_info(line)
			let new_line: string
			if (headline[0]) {
				new_line = '#'.repeat(prev_level) + ' ' + headline[1]
			} else {
				new_line = '#'.repeat(prev_level) + ' ' + line
			}
			editor.setLine(line_idx, new_line)
			this._log('Add new headline:', new_line)
		} else {
			let new_line = '#'.repeat(prev_level) + ' '
			editor.setLine(line_idx, new_line)
			editor.setCursor(line_idx, new_line.length)
			this._log('Add new headline:', new_line)
		}
	}

	_swap_lines(editor: Editor, line1_id: number, line2_id: number) {
		let temp_line = editor.getLine(line1_id)
		editor.setLine(line1_id, editor.getLine(line2_id))
		editor.setLine(line2_id, temp_line)
	}

	_move_block(editor: Editor, move_up: boolean) {
		let from_line_id = editor.getCursor('from').line
		let end_line_id = editor.getCursor('to').line
		if (from_line_id == end_line_id) {
			this._move_single_line(editor, move_up)
			return
		}
		if (from_line_id > end_line_id) {
			[from_line_id, end_line_id] = [end_line_id, from_line_id]
		}
		let move_to_line_id = move_up ? from_line_id - 1 : end_line_id + 1
		if (move_to_line_id < 0) {
			this._log('Can not move up further!')
			return
		}
		let temp_line = editor.getLine(move_to_line_id)
		let moved_block = editor.getRange({ line: from_line_id, ch: 0 }, { line: end_line_id,ch:null })
		let new_block: string
		let new_from_line_id: number = from_line_id
		let new_end_line_id: number = end_line_id

		if (move_up) {
			new_block = moved_block + '\n' + temp_line
			new_from_line_id = from_line_id - 1

		} else {
			new_block = temp_line + '\n' + moved_block
			new_end_line_id = end_line_id + 1
		}
		editor.replaceRange(new_block, { line: new_from_line_id, ch: 0 }, { line: new_end_line_id,ch:null })
		editor.setSelection({ line: move_up ? from_line_id - 1 : from_line_id + 1, ch: 0 }, { line: move_up ? end_line_id - 1 : end_line_id + 1,ch:null })
		this._log('Block moved!')
	}

	_move_single_line(editor: Editor, move_up: boolean) {

		let cur_line_id = editor.getCursor().line
		let move_to_line_id = move_up ? cur_line_id - 1 : cur_line_id + 1
		if (move_to_line_id < 0) {
			this._log('Can not move up further!')
			return
		}
		// move_to_line_id=Math.clamp(move_to_line_id,0,editor.lastLine())
		this._swap_lines(editor, cur_line_id, move_to_line_id)
		editor.setCursor(move_to_line_id)
		this._log('Origin cursor line:', cur_line_id)
		this._log('Move cursor line:', move_to_line_id)
		this._log('Line has been Moved!')
	}

	_past_code_block(editor:Editor){
		let code_text=clipboard.readText()
		let line_id=editor.getCursor().line
		let line=editor.getLine(line_id)
		if (line){
			new Notice('Please past code in an empty line!')
			return
		}
		const block_tag='```'
		let code_block=`${block_tag}\n${code_text}\n${block_tag}`
		if (editor.getLine(line_id+1)) code_block.concat('\n')
		editor.replaceRange(code_block,{line:line_id,ch:null})
		editor.setCursor({line:line_id,ch:3})
	}
	async onload() {
		navigator.clipboard.read
		await this.loadSettings();
		this._debug()
		this.addCommand({
			id: 'level-up-headline',
			name: 'Level up headline',
			hotkeys: [{ modifiers: ['Mod', 'Alt'], key: 'ArrowUp' }],
			editorCallback: (editor: Editor, view: MarkdownView) => {
				if (editor.somethingSelected()) {
					this._set_selection_headline_level(editor, true)
				} else {
					this._set_single_headline_level(editor, true)
				}
			},
		})

		this.addCommand({
			id: 'level-down-headline',
			name: 'Level down headline',
			hotkeys: [{ modifiers: ['Mod', 'Alt'], key: 'ArrowDown' }],
			editorCallback: (editor: Editor, view: MarkdownView) => {
				if (editor.somethingSelected()) {
					this._set_selection_headline_level(editor, false)
				} else {
					this._set_single_headline_level(editor, false)
				}
			},
		})


		this.addCommand({
			id: 'add-same-level-healine',
			name: 'Set headline with the same level of the previous.',
			hotkeys: [{ modifiers: ['Mod', 'Alt'], key: 'h' }],
			editorCallback: (editor: Editor, view: MarkdownView) => {
				this._add_same_level_headline(editor)
			},
		})

		this.addCommand({
			id: 'move-block-up',
			name: 'Move block upward!',
			hotkeys: [{ modifiers: ['Alt'], key: 'ArrowUp' }],
			editorCallback: (editor: Editor, view: MarkdownView) => {
				if (editor.somethingSelected()) {
					this._move_block(editor, true)
				} else {
					this._move_single_line(editor, true)
				}

			},
		})

		this.addCommand({
			id: 'move-block-down',
			name: 'Move block downward!',
			hotkeys: [{ modifiers: ['Alt'], key: 'ArrowDown' }],
			editorCallback: (editor: Editor, view: MarkdownView) => {
				if (editor.somethingSelected()) {
					this._move_block(editor, false)
				} else {
					this._move_single_line(editor, false)
				}
			},
		})

		this.addCommand({
			id: 'add-code-block',
			name: 'Add code block',
			hotkeys: [{ modifiers: ['Mod', 'Alt'], key: '`' }],
			editorCallback: (editor: Editor, view: MarkdownView) => {
				// if (editor.somethingSelected() == false && editor.getCursor().ch!=0) {
				// 	new Notice('Can not add code block here!')
				// 	return
				// }
				new AddCodeBlockModal(this.app, editor).open()
			},
		})

		this.registerEvent(this.app.workspace.on('editor-menu',(menu,editor,view)=>{
			menu.addItem((item)=>{
				item.setTitle('Past as code block')
				item.setIcon('copy')
				.onClick(async()=>{
					this._past_code_block(editor)
				})
			})
		}))

	}

	onunload() {

	}

	async loadSettings() {
		this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
	}

	async saveSettings() {
		await this.saveData(this.settings);
	}
}




export default QuickPlugin